/**********************************************************************
* 文件名称:        SignalHandler_linux.cpp
* 摘    要:       异常处理类,用于建立信号处理机制，在出现致命信号错误后,打印当前的堆栈信息以及系统相关信息。
*
***********************************************************************/
#include "SignalHandler_linux.h"
#include <cstdio>
#include <cxxabi.h>
#include <memory>
#include <execinfo.h>
#include <cstring>
#include <ucontext.h>
#include <pthread.h>
#include <sstream>
#include <string>
#include <fstream>
#include <iterator>
#include <sys/resource.h> 
#include <dirent.h>  // 用于目录操作
#include <stdexcept> // 用于异常处理
#include <iomanip>  // 用于 std::setw
//third_party lib
#define ENABLE_LIBUNWIND 

#ifdef ENABLE_LIBUNWIND
  #define UNW_LOCAL_ONLY
  extern "C"
  {
  #include "libunwind.h"
  }
#endif

namespace errors{
std::array<FailureSignal_t,10> Exceptionhandler::kFailureSignals = {{
		{ SIGSEGV, "SIGSEGV" },
		{ SIGILL, "SIGILL" },
		{ SIGFPE, "SIGFPE" },
		{ SIGABRT, "SIGABRT" },
		{ SIGBUS, "SIGBUS" },
		{ SIGTERM, "SIGTERM" },}};
std::function<void(int, siginfo_t *, void *)> Exceptionhandler::_handler;
static std::string getSignalDescription(int signal) {
    std::string signalDescription;
    switch (signal) {
        case SIGSEGV:
            signalDescription = "Segmentation fault";
            break;
        case SIGABRT:
            signalDescription = "Aborted";
            break;
        case SIGFPE:
            signalDescription = "Floating point exception";
            break;
        case SIGILL:
            signalDescription = "Illegal instruction";
            break;
        case SIGBUS:
            signalDescription = "Bus error";
            break;
        case SIGTERM:
            signalDescription = "Terminated";
            break;
        default:
            signalDescription = "Unknown signal";
            break;
    }
    return signalDescription;
}
Exceptionhandler::Exceptionhandler()
{
//	std::cout<<"Exceptionhandler build "<<std::endl;
}
Exceptionhandler::~Exceptionhandler()
{
}
#ifndef ENABLE_LIBUNWIND
static std::string demangle(const char* symbol){
	  int status = -1;
    const std::unique_ptr< char, decltype( &free ) > demangled(abi::__cxa_demangle( symbol, nullptr, nullptr, &status ), &free );
    if (demangled && status == 0) {
        return demangled.get();
    }
    else {
        return symbol;
	}
}
#endif
/***************************************************************
  *  @brief     Establish a signal processing function for the signal
  *  @param     Signal processing function handle 
  *  @note      Capture only the fatal / exit signal
***************************************************************/
void Exceptionhandler::bindingFailureSignalHandler(std::function<void(int,siginfo_t *,void *)> handler)
{
  _handler = handler;
	struct sigaction sig_action;
  memset(&sig_action, 0, sizeof(sig_action));
	/* Initialize signal set */
  sigemptyset(&sig_action.sa_mask);
	/* Provides in-depth information about the signal */
  sig_action.sa_flags |= SA_SIGINFO;
  /* Select signal processing function */
  sig_action.sa_sigaction = staticFailureSignalHandler;
//  tact.sa_handler = p;
  /* Establish a signal processing mechanism */
  for (auto &item : Exceptionhandler::kFailureSignals){
//    std::cout << "kFailureSignals:"<< item.number <<std::endl;
      if(item.number != 0){
        sigaction(item.number, &sig_action, NULL);
      }
  }
  return ;
}
void Exceptionhandler::staticFailureSignalHandler(int signum, siginfo_t *signal_info, void *ucontext) {
    if (_handler) {
         for(auto &item : Exceptionhandler::kFailureSignals){
            if(item.number == signum){
              std::cout << "[staticFailureSignalHandler]" << "signal: " << signum << " (" << getSignalDescription(signum) << ")" 
              << " (" << item.name << ")" << std::endl;
              break;
            }
         }
        _handler(signum, signal_info, ucontext);
    }
}
void Exceptionhandler::getStackTrace(std::vector<std::string> &f_stackTraceinfo, int skip_count){
#ifdef ENABLE_LIBUNWIND
  unw_cursor_t cursor;
  unw_context_t context;

	// Initialize cursor to current frame for local unwinding.

	unw_getcontext(&context);
	unw_init_local(&cursor, &context);
  skip_count++;         // Do not include the "GetStackTrace" frame
  std::stringstream ss;
	while(unw_step(&cursor) > 0) {
    unw_word_t offset, ip;
    int ret = unw_get_reg(&cursor, UNW_REG_IP, &ip);
    if (ret < 0) {
      break;
    }
		if (skip_count > 0) {
      skip_count--;
			continue;
    }
 
    char sym[256];
    if(unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {

      char* nameptr = sym;
      int status = -1;
	
			const std::unique_ptr< char, decltype( &free ) > demangled(abi::__cxa_demangle( nameptr, nullptr, nullptr, &status ), &free );
			if (demangled && status == 0) {
				nameptr = demangled.get();
			}
      ss << "0x" << std::hex << ip << " (" << nameptr << "+0x" << std::hex << offset << ")";
      f_stackTraceinfo.push_back(ss.str());
      ss.str(std::string()); // 清空 stringstream
  	}
	}
#else
	//backtrace通过读取操作系统的一个全局信息区，在多线程并发调用时，可能会造成锁冲突。
	void* addresses[256];
	//Gets the backtracking information of the current function in the program
	const int n = backtrace(addresses, std::extent< decltype(addresses) > ::value );
	//Correspond the return address to the function name
	const std::unique_ptr< char*, decltype(&free) > symbols(backtrace_symbols(addresses, n), &free);
	for(int i=0; i<n; ++i) {
		char* symbol = symbols.get()[i];
		char* end = symbol;
		while (*end) {
			++end;
		}
		while (end != symbol && *end != '+') {
			--end;
		}
		char* begin = end;
		while(begin != symbol && *begin != '(') {
			--begin;
		}
		if (begin != symbol) {
			std::string str1 = std::string(symbol, ++begin - symbol);
			*end++ = '\0';
			std::string result = str1 + demangle(begin) + '+' + end;
      f_stackTraceinfo.push_back(result); 
		}
		else {
			std::cout << symbol << std::endl;;
		}
	}
#endif
  return ;
}
bool Exceptionhandler::getSysinfo(std::string & f_sysinfo){
  struct sysinfo s_info;
  int ret;
  ret = sysinfo(&s_info);

  if(ret!=0){
    perror("get sysinfo fail");
    return false;
  }
  std::ostringstream oss;
  oss << "Uptime = " << s_info.uptime / 60 << "min \n"<< "Average load: 1 min " \
      << s_info.loads[0] << " / 5 min " << s_info.loads[1] << " / 15 min " << s_info.loads[2] << "\n"
      << "RAM: total " << s_info.totalram / 1024 << " KB / free " << s_info.freeram/ 1024 << " KB / shared " \
      << s_info.sharedram / 1024 << " KB\n" << "Memory in buffers = " << s_info.bufferram / 1024 \
      << " KB / Swap: total: " << s_info.totalswap / 1024 << " KB / free: " << s_info.freeswap / 1024 << " KB\n" \
      << "Number of processes = " << s_info.procs << "\n";

  f_sysinfo = oss.str(); 
  return true; 
}
/*
  * Get the CPU usage of the current process.
  * The CPU usage is returned as a string.
  * The string contains the following information:
  * - User time
  * - System time
  * - Children user time
  * - Children system time
  */
bool Exceptionhandler::getProcessCPUUsage(std::string &cpuUsageInfo) {
    std::ifstream stat("/proc/self/stat");
    if (!stat.is_open()) {
        cpuUsageInfo = "Error: Unable to open /proc/self/stat. Check permissions.";
        return false;
    }

    std::string line;
    std::getline(stat, line);
    if (line.empty()) {
        cpuUsageInfo = "Error: The /proc/self/stat is empty.";
        return false;
    }

    stat.close();

    std::istringstream iss(line);
    std::vector<std::string> tokens{std::istream_iterator<std::string>{iss},
                                    std::istream_iterator<std::string>{}};
    if (tokens.size() < 17) {
        cpuUsageInfo = "Error: Unexpected format in /proc/self/stat.";
        return false;
    }

    long utime = std::stol(tokens[13]);
    long stime = std::stol(tokens[14]);
    long cutime = std::stol(tokens[15]);
    long cstime = std::stol(tokens[16]);

    cpuUsageInfo = "User time: " + std::to_string(utime) + " ticks\n";
    cpuUsageInfo += "System time: " + std::to_string(stime) + " ticks\n";
    cpuUsageInfo += "Children user time: " + std::to_string(cutime) + " ticks\n";
    cpuUsageInfo += "Children system time: " + std::to_string(cstime) + " ticks\n";

    return true;
}
bool Exceptionhandler::getProcessResourceUsage(std::vector<std::string>& result)
{
    struct rusage usage;
    if (getrusage(RUSAGE_SELF, &usage) != 0)
    {
        std::cerr << "Error: Failed to get resource usage." << std::endl;
        return false;
    }

    std::stringstream ss;

    // 用户态CPU时间
    ss << "User CPU time: " << usage.ru_utime.tv_sec << "s " << usage.ru_utime.tv_usec << "us";
    result.push_back(ss.str());
    ss.str("");

    // 系统态CPU时间
    ss << "System CPU time: " << usage.ru_stime.tv_sec << "s " << usage.ru_stime.tv_usec << "us";
    result.push_back(ss.str());
    ss.str("");


    // 页面重映射次数
    ss << "Page reclaims (soft page faults): " << usage.ru_minflt;
    result.push_back(ss.str());
    ss.str("");

    // 页面错误次数
    ss << "Page faults (hard page faults): " << usage.ru_majflt;
    result.push_back(ss.str());
    ss.str("");

    // 发出的阻塞I/O操作次数
    ss << "Block input operations: " << usage.ru_inblock;
    result.push_back(ss.str());
    ss.str("");

    // 已完成的阻塞I/O操作次数
    ss << "Block output operations: " << usage.ru_oublock;
    result.push_back(ss.str());
    ss.str("");

    // IPC消息发送次数
    ss << "IPC messages sent: " << usage.ru_msgsnd;
    result.push_back(ss.str());
    ss.str("");

    // IPC消息接收次数
    ss << "IPC messages received: " << usage.ru_msgrcv;
    result.push_back(ss.str());
    ss.str("");

    // 信号量操作次数
    ss << "Semaphore operations: " << usage.ru_nvcsw + usage.ru_nivcsw;
    result.push_back(ss.str());
    ss.str("");

    // 上下文切换次数
    ss << "Voluntary context switches: " << usage.ru_nvcsw;
    result.push_back(ss.str());
    ss.str("");

    ss << "Involuntary context switches: " << usage.ru_nivcsw;
    result.push_back(ss.str());

    return true;
}
bool Exceptionhandler::getThreadStatus(std::vector<ThreadInfo>& threadStatus) {
    const std::string proc_path = "/proc/self/task";
    DIR* dir = opendir(proc_path.c_str());
    if (dir == nullptr) {
        std::cerr << "Failed to open /proc/self/task directory." << std::endl;
        return false;
    }

    struct dirent* entry;
    while ((entry = readdir(dir)) != nullptr) {
        if (entry->d_type == DT_DIR) {
            int tid = -1;
            try {
                tid = std::stoi(entry->d_name);
            } catch (const std::invalid_argument&) {
                continue;
            } catch (const std::out_of_range&) {
                continue;
            }

            if (tid > 0) {
                std::string status_file = proc_path + "/" + entry->d_name + "/status";
                std::ifstream in(status_file);
                if (in.is_open()) {
                    ThreadInfo thread_info;
                    thread_info.tid = tid;
                    std::string line;
                    while (std::getline(in, line)) {
                        if (line.substr(0, 5) == "Name:") {
                            thread_info.comm = line.substr(6);
                        } else if (line.substr(0, 6) == "State:") {
                            thread_info.state = line[7];
                            break;
                        }
                    }
                    threadStatus.push_back(thread_info);
                }
            }
        }
    }
    closedir(dir);
    return true;
}

bool Exceptionhandler::getAllInfo(ExceptionResult& result){
    if (!getSysinfo(result.sysinfo)) {
        return false;
    }

    if (!getProcessCPUUsage(result.cpuUsageInfo)) {
        return false;
    }

    if (!getProcessResourceUsage(result.resourceUsage)) {
        return false;
    }

    if (!getThreadStatus(result.threadStatus)) {
        return false;
    }
    getStackTrace(result.stackTrace);

    return true;
}
std::string Exceptionhandler::getAllInfo() {
    std::ostringstream oss;

    std::vector<std::string> stackTrace;
    getStackTrace(stackTrace);
    oss << std::left << std::setw(20) << "StackTrace:" << " ";
    for (const auto& trace : stackTrace) {
        oss << trace << " ";
    }
    oss << std::endl;

    std::string sysinfo;
    if (getSysinfo(sysinfo)) {
        oss << std::left << std::setw(20) << "Sysinfo:" << sysinfo << std::endl;
    }

    std::string cpuUsageInfo;
    if (getProcessCPUUsage(cpuUsageInfo)) {
        oss << std::left << std::setw(20) << "CPU Usage:" << cpuUsageInfo << std::endl;
    }

    std::vector<std::string> resourceUsage;
    if (getProcessResourceUsage(resourceUsage)) {
        oss << std::left << std::setw(20) << "Resource Usage:" << " ";
        for (const auto& resource : resourceUsage) {
            oss << resource << " ";
        }
        oss << std::endl;
    }

    std::vector<ThreadInfo> threadStatus;
    if (getThreadStatus(threadStatus)) {
        oss << std::left << std::setw(20) << "Thread Status:" << " ";
        for (const auto& thread : threadStatus) {
              oss << "Thread ID: " << thread.tid 
              << ", Name: " << thread.comm 
              << ", State: " << thread.state << "; ";
        }
        oss << std::endl;
    }

    return oss.str();
}

}// namespace error_handler